// Copyright 2022 AndeyaLee. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestNewConfig(t *testing.T) {
	t.Logf("%#v", newConfigFromFileAndCheck("test/faygo.ini"))
}

func TestConfigDefaultValues(t *testing.T) {
	config := &Config{}

	// Test default values
	assert.Equal(t, "http", config.NetTypes)
	assert.Equal(t, "0.0.0.0:8080", config.Addrs)
	assert.Equal(t, false, config.HttpRedirectHttps)
	assert.Equal(t, 0, config.ReadTimeout)
	assert.Equal(t, 0, config.WriteTimeout)
	assert.Equal(t, 32, config.MultipartMaxMemoryMB)
	assert.Equal(t, 0, config.SlowResponseThreshold)
	assert.Equal(t, false, config.PrintBody)
}

func TestConfigRouterDefaults(t *testing.T) {
	config := &Config{}

	// Test router default values
	assert.Equal(t, true, config.Router.RedirectTrailingSlash)
	assert.Equal(t, true, config.Router.RedirectFixedPath)
	assert.Equal(t, true, config.Router.HandleMethodNotAllowed)
	assert.Equal(t, true, config.Router.HandleOPTIONS)
	assert.Equal(t, false, config.Router.NoDefaultParams)
	assert.Equal(t, true, config.Router.DefaultUpload)
	assert.Equal(t, true, config.Router.DefaultStatic)
}

func TestConfigSessionDefaults(t *testing.T) {
	config := &Config{}

	// Test session default values
	assert.Equal(t, false, config.Session.Enable)
	assert.Equal(t, "memory", config.Session.Provider)
	assert.Equal(t, "faygosessionID", config.Session.Name)
	assert.Equal(t, "", config.Session.ProviderConfig)
	assert.Equal(t, 0, config.Session.CookieLifeSecond)
	assert.Equal(t, 300, config.Session.GcLifeSecond)
	assert.Equal(t, 3600, config.Session.MaxLifeSecond)
	assert.Equal(t, true, config.Session.AutoSetCookie)
	assert.Equal(t, "", config.Session.Domain)
	// assert.Equal(t, false, config.Session.EnableSidInHeader) // Field does not exist
	// assert.Equal(t, "Faygosessionid", config.Session.NameInHeader) // Field does not exist
	assert.Equal(t, false, config.Session.EnableSidInUrlQuery)
}

func TestConfigAPIDocDefaults(t *testing.T) {
	config := &Config{}

	// Test API doc default values
	assert.Equal(t, true, config.APIdoc.Enable)
	assert.Equal(t, "/apidoc", config.APIdoc.Path)
	assert.Equal(t, false, config.APIdoc.NoLimit)
	assert.Equal(t, false, config.APIdoc.RealIP)
	assert.Equal(t, "", config.APIdoc.Whitelist)
	assert.Equal(t, "", config.APIdoc.Desc)
	assert.Equal(t, "", config.APIdoc.Email)
	assert.Equal(t, "", config.APIdoc.TermsURL)
	assert.Equal(t, "", config.APIdoc.License)
	assert.Equal(t, "", config.APIdoc.LicenseURL)
}

func TestConfigXSRFDefaults(t *testing.T) {
	config := &Config{}

	// Test XSRF default values
	assert.Equal(t, false, config.XSRF.Enable)
	assert.Equal(t, "faygoxsrf", config.XSRF.Key)
	assert.Equal(t, 3600, config.XSRF.ExpireSecond)
}

func TestConfigValidation(t *testing.T) {
	config := &Config{
		NetTypes: "invalid",
		Addrs:    "invalid-addr",
	}

	// Test validation
	err := config.Validate()
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "invalid network type")
}

func TestConfigValidNetworkTypes(t *testing.T) {
	validTypes := []string{"http", "https", "unix_http", "unix_https", "letsencrypt", "unix_letsencrypt"}

	for _, netType := range validTypes {
		config := &Config{
			NetTypes: netType,
			Addrs:    "0.0.0.0:8080",
		}

		err := config.Validate()
		assert.NoError(t, err, "Network type %s should be valid", netType)
	}
}

func TestConfigInvalidNetworkTypes(t *testing.T) {
	invalidTypes := []string{"invalid", "ftp", "tcp", ""}

	for _, netType := range invalidTypes {
		config := &Config{
			NetTypes: netType,
			Addrs:    "0.0.0.0:8080",
		}

		err := config.Validate()
		assert.Error(t, err, "Network type %s should be invalid", netType)
	}
}

func TestConfigTimeoutValidation(t *testing.T) {
	config := &Config{
		NetTypes:     "http",
		Addrs:        "0.0.0.0:8080",
		ReadTimeout:  -1,
		WriteTimeout: -1,
	}

	err := config.Validate()
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "timeout cannot be negative")
}

func TestConfigMultipartValidation(t *testing.T) {
	config := &Config{
		NetTypes:             "http",
		Addrs:                "0.0.0.0:8080",
		MultipartMaxMemoryMB: -1,
	}

	err := config.Validate()
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "multipart max memory cannot be negative")
}

func TestConfigSessionValidation(t *testing.T) {
	config := &Config{
		NetTypes: "http",
		Addrs:    "0.0.0.0:8080",
		Session: SessionConfig{
			Enable:           true,
			Provider:         "invalid",
			CookieLifeSecond: -1,
		},
	}

	err := config.Validate()
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "invalid session provider")
}

func TestConfigValidSessionProviders(t *testing.T) {
	validProviders := []string{"memory", "file", "redis", "mysql", "postgres", "couchbase", "ledis", "ssdb", "memcache"}

	for _, provider := range validProviders {
		config := &Config{
			NetTypes: "http",
			Addrs:    "0.0.0.0:8080",
			Session: SessionConfig{
				Enable:   true,
				Provider: provider,
			},
		}

		err := config.Validate()
		assert.NoError(t, err, "Session provider %s should be valid", provider)
	}
}

func TestConfigLoadFromFile(t *testing.T) {
	// This would require creating a test config file
	// For now, test the structure
	config := &Config{}

	// Test that config can be loaded
	assert.NotNil(t, config)
}

func TestConfigMerge(t *testing.T) {
	baseConfig := &Config{
		NetTypes: "http",
		Addrs:    "0.0.0.0:8080",
	}

	overrideConfig := &Config{
		NetTypes: "https",
		Addrs:    "0.0.0.0:8443",
	}

	// Test merging configs
	baseConfig.Merge(overrideConfig)

	assert.Equal(t, "https", baseConfig.NetTypes)
	assert.Equal(t, "0.0.0.0:8443", baseConfig.Addrs)
}

func TestConfigClone(t *testing.T) {
	original := &Config{
		NetTypes: "http",
		Addrs:    "0.0.0.0:8080",
		Session: SessionConfig{
			Enable: true,
			Name:   "test",
		},
	}

	cloned := original.Clone()

	// Test that clone is independent
	assert.Equal(t, original.NetTypes, cloned.NetTypes)
	assert.Equal(t, original.Addrs, cloned.Addrs)
	assert.Equal(t, original.Session.Enable, cloned.Session.Enable)
	assert.Equal(t, original.Session.Name, cloned.Session.Name)

	// Modify clone and ensure original is unchanged
	cloned.NetTypes = "https"
	cloned.Session.Name = "modified"

	assert.Equal(t, "http", original.NetTypes)
	assert.Equal(t, "test", original.Session.Name)
	assert.Equal(t, "https", cloned.NetTypes)
	assert.Equal(t, "modified", cloned.Session.Name)
}
